せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】パーフェクトRuby 学習感想文 ~第15章 その4

それでは15章を読んでいきます。

(参考・経緯など)
パーフェクトRuby 学習感想文 ~はじめに


 

P559 コードをうつすのが大変

サンプルコードも長くなってきて、コードをうつすのが大変になってきた。
コピペできないかなー。
GitHubで作者が公開してないかなー、と探してみる。
 
著者略歴を見ると、15章担当はすがわらまさのりさん。イケメンや。
GitHubのアカウントを見るも、todoアプリのリポジトリはない。
 https://github.com/sugamasao


ぐうー><。
しかたない。写経するか。

本当だったら、意味を分かってすこしづつ書いていきたいのだけれど
最初に完成系が載っている(かつRubyルールが分からないと分かりにくい)から、
少しづつ書いていくのが難しい。


 

P557 P558 メソッド定義場所が違う?

P557 ではself.parse!はTodo::Command::Options モジュールに定義されているけど
P558 ではOptionsモジュールに定義されているよ。

どういうこと?P558 は省略して書いているの?

載っているコードはどれも一部でしかなくて
ファイル全体のコードがどこにも載っていないから、確認できないよー。。

 

Hash.newのブロック引数に例外を設定?

P559ではこういうコードが載っている。

#サブコマンドの処理をする際に、未定義のkeyを指定されたら例外を発生させる
sub_cp  Hash.new do |k, v|
	raise ArgumentError, "'#{v}' is not todo sub command."
end

解説はない。

意味わからーん。

  • newするたびに例外発生しそうだけど?
  • なんで「未定義のkey」とわかるの?
  • なんでエラーmsgにkey値でなくて、v値を出す?エラー判断対象はkeyなんでしょ?

 
Hashについて勉強する。。。

ハッシュのブロックは値が設定されていない時に実行される

公式ガイド↓

new {|hash, key| ... } -> Hash[permalink]
空の新しいハッシュを生成します。ブロックの評価結果がデフォルト値になりま す。設定したデフォルト値はHash#default_procで参照できます。

値が設定されていないハッシュ要素を参照するとその都度ブロックを 実行し、その結果を返します。 ブロックにはそのハッシュとハッシュを参照したときのキーが渡されます。

http://doc.ruby-lang.org/ja/1.9.3/class/Hash.html#S_NEW

やってみよう。

h = Hash.new {|hash, key|
                p key
             }
h["a1"] = "hoge"
h["a2"] 
p h

■実行結果

"a2"
{"a1"=>"hoge"}

え!?どういうこと?


そうなんや・・

  • Hash.newのブロックは値が設定されていないときに実行される
  • でも実行されるだけ。
  • 値が設定されていない要素はHashインスタンス自体に追加されない。

 
だから、値が設定されていない要素もインスタンスに追加したい場合は、
ブロックの中で再定義してあげないといけないんだ。

h = Hash.new {|hash, key|
                hash[key] = "defo"
             }
h["a1"] = "hoge"
h["a2"] 
p h

■実行結果

{"a1"=>"hoge", "a2"=>"defo"}


だとすると、P561のコードは、

#サブコマンドの処理をする際に、未定義のkeyを指定されたら例外を発生させる
sub_cp  Hash.new do |k, v|
	raise ArgumentError, "'#{v}' is not todo sub command."
end

これ、明らかに、

sub_cp  Hash.new do |hash, key|
	raise ArgumentError, "'#{key}' is not todo sub command."
end

のほうがいいって!
この変数名じゃ分かんないよ!(;ω;)

このコードの下には、サブコマンドの定義が書いてある。

sub_cp  Hash.new do |hash, key|
	raise ArgumentError, "'#{key}' is not todo sub command."
end
sub_cp['create'] = OptionParser.new do |opt|
	opt.on('-n VAL', '--name=VAL', 'task name') {|v| options[:name] = v}
	opt.on('-c VAL', '--content=VAL', 'task content') {|v| options[:name] = v}
end

ブロック関数は置いておいて、要はこれ、sub_cpインスタンスにkeyを定義している。
定義したkey以外はエラーになるようになってる。

 
簡単なサンプルで書くと、

h = Hash.new {|hash, key|
               raise(IndexError, "hash[#{key}] dameeeeee")
             }
h["a1"] = "hoge"

p h["a1"]
p h["a2"]

■実行結果

"hoge"
`block in <main>': hash[a2] dameeeeee (IndexError)

こんな風に、key値 "a2" は定義されていないからエラーになる。



OptionParserのブロックに渡される値

opt.on('-n VAL', '--name=VAL', 'task name') {|v| options[:name] = v}

ってところ、

'-n'でなく'-nVAL'と記述すると・・・
ブロック内に渡されるvにVALの部分の値が渡されるようになります

知ってる!ブロック関数の勉強のときにやった。
何も指定してなかったら、vには「オプションが存在するか」のboolean結果が入ってるんだよね。

ブロックの引数にはオプションが指定 されたことを示す true が渡されます

http://doc.ruby-lang.org/ja/1.8.7/library/optparse.html#optiondef


 

array.shift は破壊的

P559のコード、

cp.order!(argv)
options[:comand] = argv.shift  ←1番左の引数をoptions[:comand]に代入してる
sub_cps[options[:comand]].parse!(argv) ←1番左の引数を除いたargv

options ←optionsを返している
end

array.shiftは破壊的なのか。


 
これで P559のコードが分かったぞ!!!
でも2時間かかった!

ちなみに、OptionParser#order!を理解するために、以下のコードを実行。

irb(main):002:0> TodoSamp::Command::Options.parse!(['-v', 'create', '-n name_val'])
irb 0.0.1dayo
exit

 ⇒vコマンドだけしか実施されていない??

 
正解は、-vコマンドに渡しているブロック内にexitメソッドがあるから。

cp = OptionParser.new do |opt|
	opt.on_head('-v','--version', 'show ver') do |v|
		opt.version = TodoSamp::VERSION
		puts opt.ver + "dayo"
		exit  ←これ
	end
end

exitメソッドをコメントアウトしたら、order!の挙動の通りに動きました。

irb(main):002:0>  TodoSamp::Command::Options.parse!(['-v', 'create', '-n name_val'])
irb 0.0.1dayo
=> {:comand=>"create", :name=>" name_val"}

ていうかなぜexitする必要があるの?


 

ここまでをGitHubに登録

https://github.com/sekaiya/todo_samp
どうぞご自由に。


15章は難しすぎる。。
まだ・・・、まだ負けない!(瀕死)